#include "console.h"


#include "script/scripthelper.h"

#include "util/log.h"				// log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    

#ifdef _ENABLE_LUABIND
#include <luabind/luabind.hpp>
#endif //_ENABLE_LUABIND

#include "graphics/image.h"
#include "graphics/graphics.h"
#include "graphics/graphics_util.h"

#include "input/keyboard.h"


#include "font/font_manager.h"
#include "font/font.h"

#include <cassert>
using namespace console;
using namespace script; 

Console& Console::Instance()
{
	static Console Inst;
	return Inst;
}

Console::Console() : 
	input::KeyboardListener(),		// important.. registers itself
//	pFont(NULL),
	m_pScriptState(NULL),
	m_IsActive(false),
	m_pBackground(0),
	m_PromptPos(0.4f),
	m_TextHeight(0.35f),
	m_BackgroundHeight(0.45f),
	m_ForceDisabled(false),
	m_PrintLastCmd(true)
{
	util::Logger::Instance().SetConsoleCallback(LogMessage);
}

Console::~Console()
{
}


void	Console::ForceDisabled(bool Disable)
{
	m_ForceDisabled = Disable;
}

void Console::Init(const std::string& ConsoleFile, const std::string& ConsoleFont)
{
#if !defined(_PLATFORM_IOS) && !defined(_PLATFORM_ANDROID)
	m_pScriptState = ScriptHelper::CreateLuaState();
    if (!ScriptHelper::DoFile(*m_pScriptState, ConsoleFile))
    {
        _LOG(MSG_WARNING, "Unable to open Console file " << ConsoleFile);
	//	return;
    }

	//std::string	ConsoleBG = ScriptHelper::GetString(*m_pScriptState, "Console", "Background", "img/console.png");
	m_pBackground = GetGraphics()->LoadImageFromFile("ui_img/blue.png");

	m_TextHeight= ScriptHelper::GetFloat(*m_pScriptState, "Console", "TextHeight", m_TextHeight);
	m_PromptPos = ScriptHelper::GetFloat(*m_pScriptState, "Console", "PromptPos", m_PromptPos);
	m_BackgroundHeight = ScriptHelper::GetFloat(*m_pScriptState, "Console", "BackgroundHeight", m_BackgroundHeight);

	// register function
	Register(m_pScriptState);

//	SetFont("times new roman", 10, 14, false, 500);
#endif
}

void		Console::SetBackground(const std::string& File)
{
	m_pBackground = GetGraphics()->LoadImageFromFile(File);
}

/*
void		Console::SetFont(const std::string& Face, int Width, int Height, bool Italic, int Weight)
{
	Font* pFont = FontManager::Instance().GetFont("ConsoleFont");
	if (pFont) 
	{
		pFont = pFont;
	#ifdef _PLATFORM_WIN32	
		pFont->Resize(Face, Width, Height, Weight, HANGUL_CHARSET);
	#else
		pFont->Resize(Face, Width, Height, Weight, 0);	
	#endif //_PLATFORM_WIN32
		pFont->SetLineSpacing(2);
	} else
	{
	
#ifdef _PLATFORM_Win32
		pFont = FontManager::Instance().InitFont("ConsoleFont", Face, Width, Height, Italic, Weight, HANGUL_CHARSET);
#else
		pFont = FontManager::Instance().InitFont("ConsoleFont", Face, Width, Height, Italic, Weight, 0);
#endif //
		pFont->SetLineSpacing(2);
	}
}

void		Console::SetFont(Font*	pFont)
{
	pFont = (pFont);
}
*/

void Console::BindKey(unsigned long KeyID, const std::string& Script)
{
	// check if key already exists??
	for (unsigned int i=0; i<m_BoundKeys.size(); i++)
	{
		BoundKey&	BK = m_BoundKeys[i];
		if (BK.KeyID == KeyID)
		{
			BK.Script = Script;
			return;
		}
	}
	BoundKey	NewBK;
	NewBK.KeyID = KeyID;
	NewBK.Script = Script;
	m_BoundKeys.push_back(NewBK);
}


void Console::Shutdown()
{
	UTIL_SAFE_DELETE(m_pBackground);
	if (m_pScriptState)
	{
        LuaPlus::LuaState::Destroy( m_pScriptState );
        m_pScriptState = NULL;
	}
}

void Console::Output(const std::string&	Text)
{
	// the last character is '\n' if comming from the log
	// strip it out to remove double line breaks
	if (Text.empty()) return;

	if (Text[Text.size() - 1] == '\n')
	{
		size_t	NewSize = Text.size() -1;
		std::string NewStr = Text;
		NewStr.resize(NewSize);
		assert(NewSize < 1024);
		m_Output.push_back(NewStr);
	} else
	{
		m_Output.push_back(Text);
	}
	while (m_Output.size() > 50)
	{
		m_Output.erase(m_Output.begin());
	}
}

int Console::LogMessage( const char * str )
{
	Instance().Output(str);
	return 1;
}

void Console::DoEx(const char*  _Msg, ...)
{
	char Message[1024];
	va_list ArgP;
	va_start(ArgP, _Msg);
	vsprintf(Message, _Msg, ArgP);
	va_end(ArgP);

	Do(Message);
}
void Console::Do(const std::string&	Script)
{
	if (m_pScriptState && !Script.empty())
	{
		m_pScriptState->DoString(Script.c_str());
		if (m_PrintLastCmd)
			Output(Script);
	}
}

void Console::Render()
{
	if (m_ForceDisabled) return;
	if (!m_IsActive)
		return;
	Font*		pFont = FontManager::Instance().GetDefaultFont();
	if (!pFont) return;

	math::Vec2	TopLeft(0, 0);
	math::Vec2	BottomRight(1.0f, m_BackgroundHeight);

	if (m_pBackground)
	{
		m_pBackground->Tick();
		m_pBackground->SetTextureStage(0);
		Graphics::Vertex2D		VertA[6];
		GraphicsUtil::FillQuad(TopLeft, BottomRight-TopLeft, math::Vec2(0,0), math::Vec2(1,1), 0xFFFFFFFF, VertA);

		GetGraphics()->SetRenderState( Graphics::RS_SRC_BLEND,  Graphics::RV_BLEND_SRC_ALPHA );
		GetGraphics()->SetRenderState( Graphics::RS_DST_BLEND,  Graphics::RV_BLEND_INVSRC_ALPHA );
		GetGraphics()->DrawVertex2D(VertA, 6);

	}
	
	FontManager& FM =	FontManager::Instance();
	float	TextHeight = FM.GetDefaultFont()->GetHeight();
	int	MaxLines		= (int)((m_BackgroundHeight / TextHeight) - 1); 
	int	CurrentLines	= (int)m_Output.size();
	int StartLine		= 0;
	if (CurrentLines > MaxLines)
		StartLine = CurrentLines - MaxLines;

	assert(pFont && "Font not loaded for console!");
	int					CurrentLine = (int)m_Output.size()-1;
	math::Vec2			Pos(0.02f, m_TextHeight);
	math::Vec2			TextSize;
	while (Pos.y > 0 && CurrentLine >= 0)
	{
		math::Vec2	From;
		const std::string& Str = m_Output[CurrentLine];
		pFont->GetTextExtent(Str.c_str(), math::Vec2(1, 0), From, TextSize);
		Pos.y -= TextSize.y - (5*GraphicsUtil::InvH);
		pFont->Render(Str, Pos, math::Vec2(1,1));
		//FontManager::Instance().Print(pFont, Pos, Str.c_str());
		CurrentLine --;
	}

	FontManager::Instance().Print(pFont, math::Vec2(0.01f, m_BackgroundHeight - TextHeight), m_Input.c_str());
	FM.RenderCache();
}

void	Console::CharReceived(unsigned int Key)
{
	if (!IsActive())  return;
	char	Ch = Key & 0xFF;
	if (Ch >= ' ')
	{
		m_Input.push_back(Ch);
	}

}

void Console::KeyPressed(input::Keyboard::KEY* pKey)
{
	bool	ShiftDown = input::Keyboard::IsShiftDown();

	// check bound keys first.. always on
	for (unsigned int i=0; i<m_BoundKeys.size(); i++)
	{
		BoundKey&	BK = m_BoundKeys[i];
		if (BK.KeyID == pKey->KeyID)
		{
			Do(BK.Script);
		}
	}

	if (m_ForceDisabled) return;
	if (!m_ForceDisabled && !m_IsActive)
	{
		if (pKey->KeyID==KEY_F11)
		{
			m_IsActive = true;
		}
		return;
	}

	if (!m_IsActive) return;
	// look for special keys:
	switch (pKey->KeyID)
	{
		case KEY_LSHIFT & 0xFF:
//		case KEY_RSHIFT & 0xFF:
			break;
		case KEY_LEFT:
			
		break;
		case KEY_RIGHT:

		break;
		case KEY_UP:
			if (m_PromptIndex>0)
			{
				m_PromptIndex--;
				m_Input = m_PromptList[m_PromptIndex];
			}
		break;
		case KEY_DOWN:
			{
				size_t	Count = m_PromptList.size();
				if (Count - m_PromptIndex > 1)
				{
					m_PromptIndex++;
					m_Input = m_PromptList[m_PromptIndex];
				}
			}
		break;
		case KEY_F11:
			if (!m_ForceDisabled)
			{
				m_IsActive = false;
			}
		break;
		case KEY_INSERT:
			if (ShiftDown)
			{
				std::string		Buffer;
				if (input::Keyboard::GetClipBoard(Buffer))
				{
					m_Input = m_Input + Buffer;
				}
			}
	
		break;
		case KEY_BACKSPACE:
			if (m_Input.size() > 0)
				m_Input.resize(m_Input.size()-1);
		break;
		case KEY_RETURN:
			Output(m_Input);
			Do(m_Input);
			m_PromptList.push_back(m_Input);
			m_PromptIndex = m_PromptList.size();
			m_Input.resize(0);
			
		break;
///		default:
	}
}



void Console::Register(LuaPlus::LuaState* _pScript)
{
#ifdef _ENABLE_LUABIND
	
      lua_State * L = _pScript->GetCState ( );
		
	luabind::module(L)
    [

    luabind::class_< Console >( "Console" )
	 .def( "do",                 &Console::Do  )
    ];

    // Access file from config
	
	luabind::module(L)
    [	 luabind::def( "GetConsole",           &Console::Instance , luabind::detail::null_type() )	];
	
#endif //_ENABLE_LUABIND
}

